#include "ScriptSystem.h"

#include "ScriptComponent.h"
#include "PhysicComponents.h"
#include "PositionComponent.h"
#include "GraphicComponents.h"
#include "BulletComponent.h"
#include "ParticleComponent.h"
#include "LifeComponent.h"
#include "CharacterComponent.h"

#include "BulletSystem.h"
#include "RenderSystem.h"
#include "PhysicSystem.h"
#include "BulletSystem.h"
#include "DamageSystem.h"
#include "ParticuleSystem.h"
#include "PlayerSystem.h"

#include "GameState.h"
#include "MathUtiles.h"
#include "Base/BPL.h"

void _print(const char* txt)
{
	BPL::Logger::log(txt);
}

template<typename T> boost::shared_ptr<T> _createComponent(BPL::EntityManager* entMngr, int e)
{
	boost::shared_ptr<T> temp(new T);
	entMngr->addComponent<T>((BPL::EntityId)e, temp);

	return temp;
}

ScriptSystem::ScriptSystem()
{

}

void ScriptSystem::update(float dt)
{
	std::vector<int> toDelete;

	for(std::set<int>::iterator itr = mpt_entitiesID.begin(); itr != mpt_entitiesID.end();)
	{
		if(entityManager->hasEntity((*itr)))
		{
			ScriptComponentPtr script = entityManager->getEntity(*itr)->getAs<ScriptComponent>();
			if(script->fromFile)
			{
				try
				{
					luabind::call_function<int>(script->script, "update", dt);
					if(luabind::object_cast<bool>(luabind::globals(script->script)["toDelete"]))
						toDelete.push_back(*itr);
				}
				catch (const luabind::error &toCatch)
				{
					BPL::Logger::log(toCatch.what());
				}
			}
			else
				luaL_dostring(script->script, script->scriptStr.c_str());
 
			itr++;
		}
		else
		{
//#ifdef DEFINE_MODE
			std::string str = "BUG SCRIPT : ";
			str += boost::lexical_cast<std::string>(*itr);
			BPL::Logger::log(str.c_str());
//#endif
			itr = mpt_entitiesID.erase(itr);
		}
	}

	for(std::vector<int>::iterator toKill = toDelete.begin(); toKill != toDelete.end(); toKill++)
	{
		entityManager->killEntity(*toKill);
	}
}

void ScriptSystem::addEntity(int ent)
{
	if(!entityManager->hasEntity(ent))
		return;

    mpt_entitiesID.insert(ent);
	entityManager->getEntity(ent)->addSystem(this);
	
	// SCRIPT A CONFIGURER
	ScriptComponentPtr script = entityManager->getEntity(ent)->getAs<ScriptComponent>();

	using namespace luabind;

	script->script = luaL_newstate();

	open(script->script);

	module(script->script)
	[

	// AJOUT DES UTILES (vector sfml, model...)
		class_<sf::Vector2i>("Vec2i")
			.def(constructor<int, int>())
			.def_readwrite("x", &sf::Vector2i::x)
			.def_readwrite("y", &sf::Vector2i::y),
		class_<sf::Vector2f>("Vec2f")
			.def(constructor<float, float>())
			.def_readwrite("x", &sf::Vector2f::x)
			.def_readwrite("y", &sf::Vector2f::y),
		class_<sf::Transform>("Transform"),
		class_<sf::Transformable>("Transformable")
			.def(constructor< >())
			.def("setPosition", (void(sf::Transformable::*)(const sf::Vector2f&))&sf::Transformable::setPosition)
			.def("setOrigin", (void(sf::Transformable::*)(const sf::Vector2f&))&sf::Transformable::setOrigin)
			.def("getOrigin", &sf::Transformable::getOrigin)
			.def("getPosition", &sf::Transformable::getPosition),
		class_<sf::String>("String")
			.def(constructor<std::string>()),
		class_<sf::Font>("Font")
			.def(constructor< >())
			.def("loadFromFile", &sf::Font::loadFromFile),
		class_<sf::Drawable>("Drawable"),
		class_<sf::Text, sf::Drawable>("Text")
			.def(constructor< >())
			.def("setColor", &sf::Text::setColor)
			.def("setString", &sf::Text::setString)
			.def("setFont", &sf::Text::setFont)
			.def("setCharacterSize", &sf::Text::setCharacterSize),
		class_<sf::Sprite, sf::Transformable>("Sprite"),
		def("print", &_print),
		class_<Model>("Model")
			.def(constructor<const std::string&>())
			.def("loadModel", &Model::loadModel)
			.def("setVisible", &Model::setVisible)
			.def("setGroup", &Model::setGroup)
			.def("isPlaying", &Model::isPlaying)
			.def("setDelay", &Model::setDelay)
			.def("loop", &Model::loop)
			.def("getCurrentSprite", &Model::getCurrentSprite)
			.def("setOpacity", &Model::setOpacity),

	// AJOUT DES COMPONENTS
		class_<BPL::Component, boost::shared_ptr<BPL::Component> >("Component")
			.def(constructor< >()),
		class_<PositionComponent, BPL::Component, boost::shared_ptr<BPL::Component> >("PositionComponent")
			.def(constructor< >())
			.def_readwrite("position", &PositionComponent::m_position)
			.def_readwrite("center", &PositionComponent::m_center),
		class_<PhysicComponent, BPL::Component, boost::shared_ptr<BPL::Component> >("PhysicComponent")
			.def(constructor< >())
			.def_readwrite("normalCollision", &PhysicComponent::normalCollision)
			.def_readwrite("sizeBox", &PhysicComponent::sizeBox)
			.def_readwrite("collideXWithNoSlope", &PhysicComponent::collideXWithNoSlope)
			.def_readwrite("collideYWithNoSlope", &PhysicComponent::collideYWithNoSlope),
		class_<DynamicComponent, PhysicComponent, boost::shared_ptr<BPL::Component> >("DynamicComponent")
			.def(constructor< >())
			.def_readwrite("moveDirection", &DynamicComponent::moveDirection)
			.def_readwrite("speed", &DynamicComponent::speed),
		class_<JumpingComponent, DynamicComponent, boost::shared_ptr<BPL::Component> >("JumpingComponent")
			.def(constructor< >())
			.def_readwrite("jumpSpeed", &JumpingComponent::jumpSpeed)
			.def_readwrite("jumpLength", &JumpingComponent::jumpLength)
			.def_readwrite("jumpState", &JumpingComponent::jumpState)
			.def_readwrite("jump", &JumpingComponent::jump),
		class_<GraphicComponent, BPL::Component, boost::shared_ptr<BPL::Component> >("GraphicComponent")
			.def(constructor< >())
			.def_readwrite("layer", &GraphicComponent::layer)
			.def_readwrite("transform", &GraphicComponent::m_transform)
			.def_readwrite("sprite", &GraphicComponent::m_sprite),
		class_<ModelGraphicComponent, GraphicComponent, boost::shared_ptr<BPL::Component> >("ModelGraphicComponent")
			.def(constructor< >())
			.def_readwrite("model", &ModelGraphicComponent::model),
		class_<TextGraphicComponent, GraphicComponent, boost::shared_ptr<BPL::Component> >("TextGraphicComponent")
			.def(constructor< >())
			.def_readwrite("text", &TextGraphicComponent::text),
		class_<LifeComponent, BPL::Component, boost::shared_ptr<BPL::Component> >("LifeComponent")
			.def(constructor< >())
			.def_readwrite("life", &LifeComponent::life)
			.def_readwrite("killAfterZero", &LifeComponent::killAfterZero),
		class_<BulletComponent, BPL::Component, boost::shared_ptr<BPL::Component> >("BulletComponent")
			.def(constructor< >()),
		class_<ScriptComponent, BPL::Component, boost::shared_ptr<ScriptComponent> >("ScriptComponent")
			.def(constructor< >())
			.def_readwrite("file", &ScriptComponent::file)
			.def_readwrite("fromFile", &ScriptComponent::fromFile)
			.def_readwrite("scriptStr", &ScriptComponent::scriptStr),
		class_<CharacterComponent, BPL::Component, boost::shared_ptr<CharacterComponent> >("CharacterComponent")
			.def(constructor< >())
			.def_readwrite("bulletLeft", &CharacterComponent::bulletLeft),
		class_<ParticleComponent, BPL::Component, boost::shared_ptr<BPL::Component> >("ParticleComponent")
			.def(constructor< >()),


	// AJOUT DES SYSTEMS
		class_<BPL::System>("System")
			.def("addEntity", &BPL::System::addEntity),
		class_<BulletSystem, BPL::System>("BulletSystem"),
		class_<DamageSystem, BPL::System>("DamageSystem"),
		class_<ParticuleSystem, BPL::System>("ParticuleSystem"),
		class_<PhysicSystem, BPL::System>("PhysicSystem")
			.def("addEntity", &PhysicSystem::addEntity),
		class_<RenderSystem, BPL::System>("RenderSystem")
			.def("addEntity", &RenderSystem::addEntity),
		class_<ScriptSystem, BPL::System>("ScriptSystem")
			.def("addEntity", &ScriptSystem::addEntity),
		class_<PlayerSystem, BPL::System>("PlayerSystem")
			.def("getPlayerID", &PlayerSystem::getPlayerID),

	// AJOUT DE L'ENTITE
		class_<BPL::Entity, BPL::EntityPtr>("Entity")
			.def(constructor<BPL::EntityId>())
			.def("getID", &BPL::Entity::getID)
			.def("getAsPosition", &BPL::Entity::getAs<PositionComponent>)
			.def("getAsLife", &BPL::Entity::getAs<LifeComponent>)
			.def("getAsCharacter", &BPL::Entity::getAs<CharacterComponent>)
			.def("getAsPhysic", &BPL::Entity::getAs<PhysicComponent>)
			.def("getAsScript", &BPL::Entity::getAs<ScriptComponent>)
			.def("getAsDynamicPhysic", &BPL::Entity::getAs<DynamicComponent>)
			.def("getAsJumpingPhysic", &BPL::Entity::getAs<JumpingComponent>)
			.def("getAsGraphic", &BPL::Entity::getAs<GraphicComponent>)
			.def("getAsModelGraphic", &BPL::Entity::getAs<ModelGraphicComponent>),
	
	// AJOUT DE L'ENTITYMANAGER
		class_<BPL::EntityManager>("EntityManager")
			.def("registerDynamicEntity", &BPL::EntityManager::registerDynamicEntity)
			.def("registerStaticEntity", &BPL::EntityManager::registerStaticEntity)
			.def("registerEntity", (void(BPL::EntityManager::*)(BPL::EntityPtr))&BPL::EntityManager::registerEntity)
			.def("registerNextEntity", &BPL::EntityManager::registerNextEntity)
			.def("registerNextStaticEntity", &BPL::EntityManager::registerNextStaticEntity)
			.def("registerNextDynamicEntity", &BPL::EntityManager::registerNextDynamicEntity)
			.def("getNextAvailableID", &BPL::EntityManager::getNextAvailableID)
			.def("killEntity", &BPL::EntityManager::killEntity)
			.def("getEntity", &BPL::EntityManager::getEntity)
			.def("getStaticEntityIn", &BPL::EntityManager::getStaticEntityIn)
			.def_readwrite("tileSize", &BPL::EntityManager::sizeTile),

		def("createScriptComponent", &_createComponent<ScriptComponent>),
		def("createPhysicComponent", &_createComponent<PhysicComponent>),
		def("createJumpingComponent", &_createComponent<JumpingComponent>),
		def("createLifeComponent", &_createComponent<LifeComponent>),
		def("createCharacterComponent", &_createComponent<CharacterComponent>),
		def("createGraphicComponent", &_createComponent<GraphicComponent>),
		def("createModelGraphicComponent", &_createComponent<ModelGraphicComponent>),
		def("createTextGraphicComponent", &_createComponent<TextGraphicComponent>),
		def("createPositionComponent", &_createComponent<PositionComponent>)

	];

	luabind::globals(script->script)["entityMngr"] = entityManager;

	GameState* state = static_cast<GameState*>(mpt_parent);
	luabind::globals(script->script)["renderSystem"] = state->mpt_renderSystem;
	luabind::globals(script->script)["scriptSystem"] = state->mpt_scriptSystem;
	luabind::globals(script->script)["damageSystem"] = state->mpt_damageSystem;
	luabind::globals(script->script)["physicSystem"] = state->mpt_physicSystem;
	luabind::globals(script->script)["playerSystem"] = state->mpt_playerSystem;
	luabind::globals(script->script)["toDelete"] = false;

	luaL_dofile(script->script, script->file.c_str());
	luabind::call_function<int>(script->script, "init", ent);
}

